#include <system.h>
#include <xbee/xbee.h>
/**************************************************************//*
Interface XBee{
	Provides{
		command XBEE_init();
		command XBEE_start();
		command XBEE_reg(xbeeStartDone,xbeeTransmitDone,xbeeReceive);
		command XBEE_send(uint16_t destAddr,void*msg,uint8_t len);
	}
	Uses{
		signal void xbeeStartDone(error_t err);
		signal void xbeeTransmitDone(error_t err);
		signal message_t* xbeeReceive (message_t* buff,void*msg,uint8_t len);
	}
}*/
/***************************************************************/
/*
	MODULE XBEE uses MODULE SERIAL1 interface
		call{
			SERIAL1_init();
			SERIAL1_reg(serial1SendByteDone,serial1SendStreamDone,Serial1Receive);
			error_t SERIAL1_sendByte(uint8_t);
			error_t SERIAL1_sendStream(uint8_t *, uint8_t);
		
		}
		event{
			void serial1SendByteDone(error_t err);
			void serial1SendStreamDone(error_t);
			void serial1Receive(uint8_t));
		}
*/

//PRIVATE:

//Data Field
	#define XBEE_IDLE 0 
	#define XBEE_BUSY 1

	#define API_ATCOMMAND 	0x08
	#define API_RESPONSE 	0x88
	#define API_TRANSMIT 	0x01
	#define API_TX_STATUS 	0x89
	#define API_RECEIVE 	0x81

	#define RECV_DELIMITER 	0
	#define RECV_LEN 		1
	#define RECV_DATA		2
	#define RECV_CHECKSUM	3

	struct{
		atCommand_t cmd;
		atCommand_t* p_cmd;
	
		uint8_t state;
		uint8_t frameId;
		BOOL started;
		BOOL error;
	} transmit;

	struct{
		atCommand_t cmd;
		atCommand_t* p_cmd;

		message_t msg;
		message_t* p_msg;
	
		uint8_t state;
		uint8_t len;
		uint16_t max_len;
	} recv;


	uint8_t CMD_MY[]="MY";
	uint8_t CMD_MM[]="MM";
	uint8_t CMD_WR[]="WR";

	void(*xbee_startDone)(error_t);
	void(*xbee_transmitDone)(error_t);

	message_t *(*xbee_receive)(message_t*,void* msg,uint8_t len);

//Function Member
	error_t atCommandMY(uint8_t* cmd,uint16_t aos_add);
	error_t stCommandMM(uint8_t* cmd,uint8_t mac_mode);
	error_t atCommandWR(uint8_t* cmd);	
	error_t rfTransmit(uint16_t dest_add,uint8_t option,uint8_t* payload,uint8_t len);

	void atResponse(uint8_t frameId,uint8_t* atCmd,uint8_t status,uint16_t value);
	void rfTransmitStatus(uint8_t id,uint8_t status);
	void cmd_recieve(atCommand_t* packet,uint16_t payload_len);
	void rfReceive(uint16_t src,uint8_t rssi,uint8_t option,uint8_t* payload,uint8_t len);

	void XBEE_defaultStartDone(error_t err);
	void XBEE_defaultTransmitDone(error_t err);
	message_t *XBEE_defaultReceive(message_t*,void* msg,uint8_t len);

	void api_receive(uint8_t data); 
	error_t api_send(uint8_t api_id,atCommand_t* packet,uint16_t len); 

/**************************************************************/
/*            Client Function Implementation                  */
/*                      (Provides)                            */					
/**************************************************************/
error_t XBEE_init(){

	SERIAL0_init();

	transmit.started = FALSE;
	transmit.state = XBEE_IDLE;

	transmit.p_cmd=&(transmit.cmd);
	transmit.frameId = 0;

	recv.p_cmd = &(recv.cmd);
	recv.state = RECV_DELIMITER;
	recv.p_msg = &(recv.msg);

	xbee_startDone 		= XBEE_defaultStartDone;
	xbee_transmitDone 	= XBEE_defaultTransmitDone;
	xbee_receive 		= XBEE_defaultReceive;

	return SUCCESS;
}
error_t XBEE_start(){

	if(transmit.started){
		return FAIL;
	}
	
	transmit.started = TRUE;

	SERIAL0_reg(NULL,NULL,api_receive);	

	return atCommandMY(CMD_MY,XBEE_MAC_SRC_ADD);
}
void XBEE_reg(void(*startDone)(error_t),void(*transmitDone)(error_t),
			  message_t *(*receive)(message_t*,void* msg,uint8_t len)){
	if(startDone!=NULL){
		xbee_startDone 		= startDone;
	}
	else{
		xbee_startDone 		= XBEE_defaultStartDone;
	}
	if(transmitDone!=NULL){
		xbee_transmitDone 	= transmitDone;
	}
	else{
		xbee_transmitDone 	= XBEE_defaultTransmitDone;
	}
	if(receive!=NULL){
		xbee_receive		= receive;
	}
	else{
		xbee_receive 		= XBEE_defaultReceive;
	}
}
error_t XBEE_send(uint16_t dest_add, void* msg,uint8_t len){
	uint8_t option = 1;

	if(transmit.state != XBEE_IDLE){
		return FAIL;
	}

	transmit.state = XBEE_BUSY;

	return rfTransmit(dest_add,option,(uint8_t*)msg,len);
}
/**************************************************************/
/*            Private Function Implementation                 */
/*                      (Internals)                           */					
/**************************************************************/
void cmd_recieve(atCommand_t* packet,uint16_t payload_len){

	atHeader_t* header = (atHeader_t*)(packet);

	if(header->id == API_RESPONSE){
		atResponse_t* response = (atResponse_t*)(packet->payload);
		uint16_t value;

		value = response->value[0];
		value <<= 8;
		value |= response->value[1];

		atResponse(response->frameId,response->command,response->status,value);
	}
	else if(header->id == API_TX_STATUS){
		rfTransmitS_t* tx_status = (rfTransmitS_t*)(packet->payload);
		rfTransmitStatus(tx_status->frameId,tx_status->status); 
	}
	else if(header->id == API_RECEIVE){

		rfReceive_t* recv_msg = (rfReceive_t*)(packet->payload);
		
		uint16_t src = 0;
		uint8_t msg_len;

		src   = recv_msg->src[0];
		src <<= 8;
		src  |= recv_msg->src[1];

		msg_len = (uint8_t)(payload_len);
		msg_len -= 4;

		rfReceive(src,recv_msg->rssi,recv_msg->option,recv_msg->payload,msg_len);
	}
}
error_t atCommandMY(uint8_t* cmd,uint16_t aos_add){
	
	atCommandMY_t* command_MY;

	if(transmit.state != XBEE_IDLE){
		return FAIL;
	}
	transmit.state = XBEE_BUSY;

	command_MY = (atCommandMY_t*)(transmit.p_cmd->payload);

	if(transmit.frameId == 0)
		transmit.frameId = 1;

	command_MY->frameId 	= transmit.frameId++;
	command_MY->command[0]	= cmd[0];
	command_MY->command[1]	= cmd[1];
	command_MY->value[0]	= (uint8_t)(aos_add>>8);
	command_MY->value[1]	= (uint8_t)(aos_add);

	return api_send(API_ATCOMMAND,transmit.p_cmd,sizeof(atCommandMY_t));
}

error_t atCommandMM(uint8_t* cmd,uint8_t mac_mode){
	
	atCommandMM_t* command_MM;

	if(transmit.state!=XBEE_IDLE){
		return FAIL;
	}

	transmit.state = XBEE_BUSY;
	
	command_MM = (atCommandMM_t*)(transmit.p_cmd->payload);
	
	if(transmit.frameId==0)
		transmit.frameId=1;
	
	command_MM->frameId    = transmit.frameId++;
	command_MM->command[0] = cmd[0];
	command_MM->command[1] = cmd[1];
	command_MM->value	   = mac_mode;		

	return api_send(API_ATCOMMAND,transmit.p_cmd,sizeof(atCommandMM_t));
}
error_t atCommandWR(uint8_t* cmd){
	
	atCommandWR_t* command_WR;

	if(transmit.state!=XBEE_IDLE){
		return FAIL;
	}

	transmit.state = XBEE_BUSY;

	command_WR = (atCommandWR_t*)(transmit.p_cmd->payload);
	
	if(transmit.frameId==0)
		transmit.frameId = 1;

	command_WR->frameId = transmit.frameId++;
	command_WR->command[0] = cmd[0];
	command_WR->command[1] = cmd[1];
	
	return api_send(API_ATCOMMAND,transmit.p_cmd, sizeof(atCommandWR_t));
}

error_t rfTransmit(uint16_t dest_add,uint8_t option,uint8_t* payload,uint8_t len){
	
	rfTransmit_t* tx_cmd = (rfTransmit_t*)transmit.p_cmd->payload;
	uint8_t i;

	transmit.state = XBEE_BUSY;	

	if(len>100){
		return FAIL;
	}
	
	if(transmit.frameId==0)
		transmit.frameId=1;

	tx_cmd->frameId = transmit.frameId++;
	tx_cmd->dest[0] = (uint8_t)(dest_add>>8);
	tx_cmd->dest[1] = (uint8_t)(dest_add);
	tx_cmd->option	= option;

	for(i=0;i<len;i++){
		tx_cmd->payload[i]=payload[i]; 	
	}
			
	return api_send(API_TRANSMIT,transmit.p_cmd,4+len);
}

void rfReceive(uint16_t src,uint8_t rssi,uint8_t option,uint8_t* payload,uint8_t api_payload_len){

	msg_metadata_t* metadata = (msg_metadata_t*)(recv.p_msg->metadata);
	uint8_t* api_payload = (uint8_t*)recv.p_msg;

	uint8_t i;

	metadata->rssi = rssi;	

	for(i=0;i<api_payload_len;i++){
		api_payload[i] = payload[i];
	}

	recv.p_msg = (*xbee_receive)(recv.p_msg,recv.p_msg->payload,api_payload_len); 
}

error_t api_send(uint8_t api_id,atCommand_t* packet,uint16_t len){

	atHeader_t* header = (atHeader_t*)(packet);
	
	uint8_t checksum;
	uint16_t i;
	
	header-> delimiter	= 0x7E;
	header-> len[0]		= (uint8_t)((len+1)>>8);
	header-> len[1]		= (uint8_t)(len+1);
	header-> id 		= api_id;

	checksum = api_id;
	for(i=0;i<len;i++){
		checksum += packet->payload[i];
	}
	checksum = 0xFF - checksum;

	packet->payload[len] = checksum;

	return SERIAL0_sendStream((uint8_t*)packet, 5+len);
}

void atResponse(uint8_t frameId,uint8_t* atCmd,uint8_t status,uint16_t value){
	
	transmit.state = XBEE_IDLE;
	
	if(atCmd[0]=='M' && atCmd[1]=='Y'){
		atCommandMM(CMD_MM,MAC_MODE);
	}
	else if(atCmd[0]=='M' && atCmd[1]=='M'){
		atCommandWR(CMD_WR);
	}
	else if(atCmd[0]=='W' && atCmd[1]=='R'){
		transmit.started=TRUE;
		(*xbee_startDone)(SUCCESS);
	}	
}

void rfTransmitStatus(uint8_t id,uint8_t status){
	
	transmit.state = XBEE_IDLE;

	if(status==0)
		transmit.error=SUCCESS;
	else
		transmit.error=FAIL;

	(*xbee_transmitDone)(transmit.error);
}

/**************************************************************/
/*            System Function Implementation                  */
/*                    (Used by system)                        */					
/**************************************************************/
void api_receive(uint8_t data){
	
	atHeader_t* header = (atHeader_t*)(recv.p_cmd);
    
	uint8_t checksum;
	uint16_t i;

	switch(recv.state){

		case RECV_DELIMITER:
				if(data==0x7E){
				recv.state = RECV_LEN;
				recv.len = 0;
				
				header->delimiter = data;
			}
		break;

		case RECV_LEN:
			if(recv.len==0){
				recv.len=1;
				
				header->len[0]=data;
			}
			else{
				recv.len = 0;
				header->len[1]=data;

				recv.max_len = header->len[0];
				recv.max_len <<= 8;
				recv.max_len |= header->len[1];
				
				if(recv.max_len>100){
					recv.state = RECV_DELIMITER;
					break;
				}
				recv.state = RECV_DATA; 
			}
		break;

		case RECV_DATA:
			if(recv.len == 0){
				header->id = data;
			}
			else{
				recv.p_cmd->payload[recv.len-1]=data;
			}
			recv.len++;
			if(recv.len<recv.max_len){
				break;
			}
			
			recv.state = RECV_CHECKSUM; 
		break;

		case RECV_CHECKSUM:
			recv.p_cmd->checksum = data;

			checksum = recv.p_cmd->checksum + header->id;
			for(i=0;i<(recv.max_len - 1);i++){
				checksum += recv.p_cmd->payload[i];
			}
			
			if(checksum==0xFF){
				cmd_recieve(recv.p_cmd,(recv.max_len - 1));
			}
			recv.state = RECV_DELIMITER;
		break;
	}
}


void XBEE_defaultStartDone(error_t err){
}
void XBEE_defaultTransmitDone(error_t err){
}
message_t *XBEE_defaultReceive(message_t* msg,void* payload,uint8_t len){
  return msg;
}
